/**
 * 日历类
 * @param {Object} option 配置
 * @param {Object} lang 语言配置
 */
/**
 * 如果当前年份能被4整除但是不能被100整除或者能被400整除，即可确定为闰年，返回1，否则返回0
 */
function isLeap(year) {
	return year % 4 == 0 ? (year % 100 != 0 ? 1 : (year % 400 == 0 ? 1 : 0)) : 0;
}
function Calendar( option={},lang={} ){

	/**
	 * 配置
	 * @type {Object}
	 */
	this.option = {
		el: null,      // 元素
		old:true,      // 是否显示 老历
		wheel:true,    // 是否开启滚轮上下月
		time: null,    // 时间
		oper:true,     // 是否显示操作栏
		oldCal:false,   // 是否显示老历
		holiday:false,  // 是否显示节假日
		date: null,    // 日期 new Date()
		year: 0,       // 当前年
		month: 0,      // 当前月
		day: 0,        // 当前天
		week: 0,        // 当前周期

		// 下方为系统配置
		_dom:null,      // 日历dom
		_operEl:null,   // 操作栏元素
		_calEl:null,    // 日历元素
		_dateEl:null,   // 日期元素
		_weekEl:null,   // 周期元素
		_dayEl:null,    // 日子元素

	}
	this.option = Object.assign( this.option, option );

	/**
	 * 语言替换
	 * 需要支持多语言
	 */
	this.lang = {
		// 周期
		weekArr:['日','一','二','三','四','五','六'],
		// 周期数字
		weekArrNum:[7,1,2,3,4,5,6],
		month:"月",
		ten:"初十",
		twenty:"二十",
		thirty:"三十",
		// 节假日
		holiday:{
			"1-1":"元旦节",
			"2-2":"湿地日",
			"2-14":"情人节",
			"3-8":"妇女节",
			"3-12":"植树节",
			"4-1":"愚人节",
			"4-12":"复活节",
			"4-22":"地球日",
			"4-23":"读书日",
			"4-24":"航天日",
			"5-1":"劳动节",
			"5-4":"青年节",
			"5-10":"母亲节",
			"5-12":"护士节",
			"5-18":"国际博物馆日",
			"5-19":"旅游日",
			"5-31":"无烟日",
			"6-1":"儿童节",
			"7-1":"建党节",
			"8-1":"建军节",
			"9-10":"教师节",
			"10-1":"国庆节",
			"11-8":"记者节",
			"11-17":"学生日",
			"12-1":"艾滋病日",
			"12-4":"宪法日",
			"12-24":"平安夜",
			"12-25":"圣诞节"
		},
		// 农历日
		oldHoliday:{
			"1-1":"春节",
			"1-15":"元宵节",
			"2-2":"龙抬头",
			"5-5":"端午节",
			"7-7":"七夕节",
			"8-15":"中秋节",
			"9-9":"重阳节",
			"12-8":"腊八节"
		},
		// 除夕
		prevNew:{
			"12-29":"除夕",
			"12-30":"除夕",
			"12-31":"除夕"
		},
		// 天干地支之天干速查表
		Gan:["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"],
		// 天干地支之地支速查表
		Zhi:["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"],
		// 天干地支之地支速查表<=>生肖
		Animals:["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"],
		// 24节气速查表
		solarTerm:["小寒","大寒","立春","雨水","惊蛰","春分","清明节","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"],
		// 12 星座
		constellation:['水瓶','双鱼','白羊','金牛','双子','巨蟹','狮子','处女','天枰','天蝎','射手','魔蝎'],
		// 数字转中文速查表
		nStr1:['日','一','二','三','四','五','六','七','八','九','十'],
		// 日期转农历称呼速查表
		nStr2:['初','十','廿','卅'],
		// 月份转农历称呼速查表
		nStr3:['正','一','二','三','四','五','六','七','八','九','十','冬','腊'],
	}
	this.lang = Object.assign( this.lang, lang );
	
	// -----------------------------------------------------------
	// 基本
	// -----------------------------------------------------------
	this.option.el = typeof(this.option.el)=='string'?document.querySelector(this.option.el):this.option.el;
	this.option.date = this.option.time ? new Date(this.option.time) : new Date();
	this.option.year = this.option.date.getFullYear();
	this.option.month = this.option.date.getMonth();
	this.option.day = this.option.date.getDate();
	this.option.week = this.lang.weekArrNum[this.option.date.getDay()];
	this._holiday = Object.keys(this.lang.holiday);
	this._oldHoliday = Object.keys(this.lang.oldHoliday);
	// -----------------------------------------------------------
	// 基本 END
	// -----------------------------------------------------------


	// ----------------------------------------------------------
	// 农历相关
	// ----------------------------------------------------------
	// 农历1900-2100的润大小信息表
	this.lunarInfo = [
		0x04bd8,0x04ae0,0x0a570,0x054d5,0x0d260,0x0d950,0x16554,0x056a0,0x09ad0,0x055d2,//1900-1909
		0x04ae0,0x0a5b6,0x0a4d0,0x0d250,0x1d255,0x0b540,0x0d6a0,0x0ada2,0x095b0,0x14977,//1910-1919
		0x04970,0x0a4b0,0x0b4b5,0x06a50,0x06d40,0x1ab54,0x02b60,0x09570,0x052f2,0x04970,//1920-1929
		0x06566,0x0d4a0,0x0ea50,0x06e95,0x05ad0,0x02b60,0x186e3,0x092e0,0x1c8d7,0x0c950,//1930-1939
		0x0d4a0,0x1d8a6,0x0b550,0x056a0,0x1a5b4,0x025d0,0x092d0,0x0d2b2,0x0a950,0x0b557,//1940-1949
		0x06ca0,0x0b550,0x15355,0x04da0,0x0a5b0,0x14573,0x052b0,0x0a9a8,0x0e950,0x06aa0,//1950-1959
		0x0aea6,0x0ab50,0x04b60,0x0aae4,0x0a570,0x05260,0x0f263,0x0d950,0x05b57,0x056a0,//1960-1969
		0x096d0,0x04dd5,0x04ad0,0x0a4d0,0x0d4d4,0x0d250,0x0d558,0x0b540,0x0b6a0,0x195a6,//1970-1979
		0x095b0,0x049b0,0x0a974,0x0a4b0,0x0b27a,0x06a50,0x06d40,0x0af46,0x0ab60,0x09570,//1980-1989
		0x04af5,0x04970,0x064b0,0x074a3,0x0ea50,0x06b58,0x055c0,0x0ab60,0x096d5,0x092e0,//1990-1999
		0x0c960,0x0d954,0x0d4a0,0x0da50,0x07552,0x056a0,0x0abb7,0x025d0,0x092d0,0x0cab5,//2000-2009
		0x0a950,0x0b4a0,0x0baa4,0x0ad50,0x055d9,0x04ba0,0x0a5b0,0x15176,0x052b0,0x0a930,//2010-2019
		0x07954,0x06aa0,0x0ad50,0x05b52,0x04b60,0x0a6e6,0x0a4e0,0x0d260,0x0ea65,0x0d530,//2020-2029
		0x05aa0,0x076a3,0x096d0,0x04afb,0x04ad0,0x0a4d0,0x1d0b6,0x0d250,0x0d520,0x0dd45,//2030-2039
		0x0b5a0,0x056d0,0x055b2,0x049b0,0x0a577,0x0a4b0,0x0aa50,0x1b255,0x06d20,0x0ada0,//2040-2049
		/**Add By JJonline@JJonline.Cn**/
		0x14b63,0x09370,0x049f8,0x04970,0x064b0,0x168a6,0x0ea50, 0x06b20,0x1a6c4,0x0aae0,//2050-2059
		0x0a2e0,0x0d2e3,0x0c960,0x0d557,0x0d4a0,0x0da50,0x05d55,0x056a0,0x0a6d0,0x055d4,//2060-2069
		0x052d0,0x0a9b8,0x0a950,0x0b4a0,0x0b6a6,0x0ad50,0x055a0,0x0aba4,0x0a5b0,0x052b0,//2070-2079
		0x0b273,0x06930,0x07337,0x06aa0,0x0ad50,0x14b55,0x04b60,0x0a570,0x054e4,0x0d160,//2080-2089
		0x0e968,0x0d520,0x0daa0,0x16aa6,0x056d0,0x04ae0,0x0a9d4,0x0a2d0,0x0d150,0x0f252,//2090-2099
		0x0d520];//2100
	// 公历每个月份的天数普通表
	this.solarMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
	
	// 1900-2100各年的24节气日期速查表
	this.sTermInfo = ['9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f',
       '97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
       '97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f','b027097bd097c36b0b6fc9274c91aa',
       '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd0b06bdb0722c965ce1cfcc920f',
       'b027097bd097c36b0b6fc9274c91aa','9778397bd19801ec9210c965cc920e','97b6b97bd19801ec95f8c965cc920f',
       '97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2','9778397bd197c36c9210c9274c91aa',
       '97b6b97bd19801ec95f8c965cc920e','97bd09801d98082c95f8e1cfcc920f','97bd097bd097c36b0b6fc9210c8dc2',
       '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec95f8c965cc920e','97bcf97c3598082c95f8e1cfcc920f',
       '97bd097bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e',
       '97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
       '97b6b97bd19801ec9210c965cc920e','97bcf97c3598082c95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722',
       '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f',
       '97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
       '97bcf97c359801ec95f8c965cc920f','97bd097bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
       '97b6b97bd19801ec9210c965cc920e','97bcf97c359801ec95f8c965cc920f','97bd097bd07f595b0b6fc920fb0722',
       '9778397bd097c36b0b6fc9210c8dc2','9778397bd19801ec9210c9274c920e','97b6b97bd19801ec95f8c965cc920f',
       '97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
       '97b6b97bd19801ec95f8c965cc920f','97bd07f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
       '9778397bd097c36c9210c9274c91aa','97b6b97bd19801ec9210c965cc920e','97bd07f1487f595b0b0bc920fb0722',
       '7f0e397bd097c36b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
       '97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
       '97b6b97bd19801ec9210c965cc920e','97bcf7f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
       '9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e','97bcf7f1487f531b0b0bb0b6fb0722',
       '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b97bd19801ec9210c965cc920e',
       '97bcf7f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
       '97b6b97bd19801ec9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
       '9778397bd097c36b0b6fc9210c91aa','97b6b97bd197c36c9210c9274c920e','97bcf7f0e47f531b0b0bb0b6fb0722',
       '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','9778397bd097c36c9210c9274c920e',
       '97b6b7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c36b0b6fc9210c8dc2',
       '9778397bd097c36b0b70c9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
       '7f0e397bd097c35b0b6fc9210c8dc2','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
       '7f0e27f1487f595b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa',
       '97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
       '9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
       '7f0e397bd097c35b0b6fc920fb0722','9778397bd097c36b0b6fc9274c91aa','97b6b7f0e47f531b0723b0b6fb0721',
       '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9274c91aa',
       '97b6b7f0e47f531b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
       '9778397bd097c36b0b6fc9210c91aa','97b6b7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
       '7f0e397bd07f595b0b0bc920fb0722','9778397bd097c36b0b6fc9210c8dc2','977837f0e37f149b0723b0787b0721',
       '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f5307f595b0b0bc920fb0722','7f0e397bd097c35b0b6fc9210c8dc2',
       '977837f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0721','7f0e37f1487f595b0b0bb0b6fb0722',
       '7f0e397bd097c35b0b6fc9210c8dc2','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
       '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722','977837f0e37f14998082b0787b06bd',
       '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd097c35b0b6fc920fb0722',
       '977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
       '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
       '7f0e27f1487f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14998082b0787b06bd',
       '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0b0bb0b6fb0722','7f0e397bd07f595b0b0bc920fb0722',
       '977837f0e37f14998082b0723b06bd','7f07e7f0e37f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
       '7f0e397bd07f595b0b0bc920fb0722','977837f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b0721',
       '7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f595b0b0bb0b6fb0722','7f0e37f0e37f14898082b0723b02d5',
       '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e37f1487f531b0b0bb0b6fb0722',
       '7f0e37f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
       '7f0e37f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
       '7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e37f14898082b072297c35',
       '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722',
       '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f149b0723b0787b0721',
       '7f0e27f1487f531b0b0bb0b6fb0722','7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14998082b0723b06bd',
       '7f07e7f0e47f149b0723b0787b0721','7f0e27f0e47f531b0723b0b6fb0722','7f0e37f0e366aa89801eb072297c35',
       '7ec967f0e37f14998082b0723b06bd','7f07e7f0e37f14998083b0787b0721','7f0e27f0e47f531b0723b0b6fb0722',
       '7f0e37f0e366aa89801eb072297c35','7ec967f0e37f14898082b0723b02d5','7f07e7f0e37f14998082b0787b0721',
       '7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66aa89801e9808297c35','665f67f0e37f14898082b0723b02d5',
       '7ec967f0e37f14998082b0787b0721','7f07e7f0e47f531b0723b0b6fb0722','7f0e36665b66a449801e9808297c35',
       '665f67f0e37f14898082b0723b02d5','7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721',
       '7f0e36665b66a449801e9808297c35','665f67f0e37f14898082b072297c35','7ec967f0e37f14998082b0787b06bd',
       '7f07e7f0e47f531b0723b0b6fb0721','7f0e26665b66a449801e9808297c35','665f67f0e37f1489801eb072297c35',
       '7ec967f0e37f14998082b0787b06bd','7f07e7f0e47f531b0723b0b6fb0721','7f0e27f1487f531b0b0bb0b6fb0722'];
	/**
	 * 返回农历y年一整年的总天数
	 * @param  {number} year
	 * @return number
	 */
	Calendar.prototype.lYearDays = (year)=>{
		let i,sum=348;
		for(i=0x8000; i>0x8; i>>=1){
			sum += (this.lunarInfo[year-1900] & i)? 1: 0;
		}
		return (sum+this.leapDays(year));
	}
	/**
	 * 返回农历y年闰月是哪个月；若y年没有闰月 则返回0
	 * @param  {number} year
	 * @return number (0-12)
	 */
	Calendar.prototype.leapMonth = (year)=>{
		return(this.lunarInfo[year-1900] & 0xf);
	}
	/**
	 * 返回农历y年闰月的天数 若该年没有闰月则返回0
	 * @param  {number} year
	 * @return number
	 */
	Calendar.prototype.leapDays = (year)=>{
		if(this.leapMonth(year)) {
			return((this.lunarInfo[year-1900] & 0x10000)? 30: 29);
		}
		return 0;
	}
	/**
	 * 返回农历y年m月（非闰月）的总天数，计算m为闰月时的天数请使用leapDays方法
	 * @param  {number} year
	 * @param  {number} month
	 * @return number
	 */
	Calendar.prototype.monthDays = (year,month)=>{
		if(month>12 || month<1) {return -1}//月份参数从1至12，参数错误返回-1
		return( (this.lunarInfo[year-1900] & (0x10000>>month))? 30: 29 );
	}
	/**
	 * 返回公历(!)y年m月的天数
	 * @param  {number} year
	 * @param  {number} month
	 * @return number (-1、28、29、30、31)
	 */
	Calendar.prototype.solarDays = (year,month)=>{
		if(month>12 || month<1) {return -1} //若参数错误 返回-1
		let ms = month-1;
		if(ms==1) { //2月份的闰平规律测算后确认返回28或29
			return(((year%4 == 0) && (year%100 != 0) || (year%400 == 0))? 29: 28);
		}else{
			return(this.solarMonth[ms]);
		}
	}
	/**
	 * 农历年份转换为干支纪年
	 * @param  {number} lYear 农历年的 年份数
	 * @return string
	 */
	Calendar.prototype.toGanZhiYear = (lYear)=>{
		let ganKey = (lYear - 3) % 10;
		let zhiKey = (lYear - 3) % 12;
		if(ganKey == 0) ganKey = 10;//如果余数为0则为最后一个天干
		if(zhiKey == 0) zhiKey = 12;//如果余数为0则为最后一个地支
		return this.lang.Gan[ganKey-1] + this.lang.Zhi[zhiKey-1];
	}
	/**
	 * 公历月、日判断所属星座
	 * @param  {number} cMonth [0~11]
	 * @param  {number} cDay   [1~31]
	 * @return string
	 */
	Calendar.prototype.toAstro = (cMonth,cDay)=>{
		let arr = [[1.20,2.18],[2.19,3.20],[3.21,4.19],[4.20,5.20],[5.21,6.21],[6.22,7.22],[7.23,8.22],[8.23,9.22],[9.23,10.23],[10.24,11.22],[11.23,12.21],[12.22,1.19]];
		let dateFloat = parseFloat( cMonth+1+'\.'+cDay );
		for (var i = 0; i < 12; i++) {
			if( dateFloat >= arr[i][0] && dateFloat <= arr[i][1] ){
				return( this.lang.constellation[i] ); // 返回星座
			}
		}
		return this.lang.constellation[this.lang.constellation.length-1]; // 如果都没有，则返回魔蝎
	}
	/**
	 * 传入offset偏移量返回干支
	 * @param  {[type]} offset 相对甲子的偏移量
	 * @return string
	 */
	Calendar.prototype.toGanZhi = (offset)=>{
		return this.lang.Gan[offset%10] + this.lang.Zhi[offset%12];
	}
	/**
	 * 传入公历(!)y年获得该年第n个节气的公历日期
	 * @param  {[type]} y [公历年(1900-2100)]
	 * @param  {[type]} n [二十四节气中的第几个节气(1~24)；从n=1(小寒)算起]
	 * @return number
	 */
	Calendar.prototype.getTerm = (y,n)=>{
		if(y<1900 || y>2100) {return -1;}
		if(n<1 || n>24) {return -1;}
		let _table = this.sTermInfo[y-1900];
		let _info = [
			parseInt('0x'+_table.substr(0,5)).toString() ,
			parseInt('0x'+_table.substr(5,5)).toString(),
			parseInt('0x'+_table.substr(10,5)).toString(),
			parseInt('0x'+_table.substr(15,5)).toString(),
			parseInt('0x'+_table.substr(20,5)).toString(),
			parseInt('0x'+_table.substr(25,5)).toString()
		];

		let _calday = [
			_info[0].substr(0,1),
			_info[0].substr(1,2),
			_info[0].substr(3,1),
			_info[0].substr(4,2),
			_info[1].substr(0,1),
			_info[1].substr(1,2),
			_info[1].substr(3,1),
			_info[1].substr(4,2),
			_info[2].substr(0,1),
			_info[2].substr(1,2),
			_info[2].substr(3,1),
			_info[2].substr(4,2),
			_info[3].substr(0,1),
			_info[3].substr(1,2),
			_info[3].substr(3,1),
			_info[3].substr(4,2),
			_info[4].substr(0,1),
			_info[4].substr(1,2),
			_info[4].substr(3,1),
			_info[4].substr(4,2),
			_info[5].substr(0,1),
			_info[5].substr(1,2),
			_info[5].substr(3,1),
			_info[5].substr(4,2),
			];
		return parseInt(_calday[n-1]);
	}
	/**
	 * 传入农历数字月份返回汉语通俗表示法
	 * @param  {number} month
	 * @return string
	 */
	Calendar.prototype.toChinaMonth = (month)=>{
		if(month>12 || month<1) {return -1} //若参数错误 返回-1
		// let s = this.lang.nStr3[month-1];
		let s = this.lang.nStr3[month];
		s += this.lang.month;
		return s;
	}
	/**
	 * 传入农历日期数字返回汉字表示法
	 * @param  {number} d
	 * @return string
	 */
	Calendar.prototype.toChinaDay = (d)=>{
		let s;
		switch(d){
			case 10:
				s = this.lang.ten; break;
			case 20:
				s = this.lang.twenty; break;
			case 30:
				s = this.lang.thirty; break;
			default:
				s = this.lang.nStr2[Math.floor(d/10)];
				s += this.lang.nStr1[d%10];
			break;
		}
		return s;
	}
	/**
	 * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
	 * @param  {number} y
	 * @return string
	 */
	Calendar.prototype.getAnimal = (y)=>{
		return this.lang.Animals[(y - 4) % 12];
	}

	/**
	 * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
	 * @param  {number} y
	 * @param  {number} m
	 * @param  {number} d
	 * @return json | object
	 */
	Calendar.prototype.solar2lunar = (y,m,d)=>{
		if(y<1900 || y>2100) {return -1;}//年份限定、上限
		if(y==1900&&m==1&&d<31) {return -1;}//下限
		if(!y) { //未传参 获得当天
			var objDate = new Date();
		}else {
			var objDate = new Date(y,parseInt(m)-1,d);
		}
		let i,leap=0,temp=0;
			y = objDate.getFullYear(),m = objDate.getMonth()+1,d = objDate.getDate();
		let offset  = (Date.UTC(objDate.getFullYear(),objDate.getMonth(),objDate.getDate()) - Date.UTC(1900,0,31))/86400000;
		for(i=1900; i<2101 && offset>0; i++) { temp=this.lYearDays(i); offset-=temp;}
		if(offset<0) { offset+=temp; i--; }

		// 是否今天
		let isTodayObj = new Date(),isToday=false;
		if(isTodayObj.getFullYear()==y && isTodayObj.getMonth()+1==m && isTodayObj.getDate()==d) {
			isToday = true;
		}
		// 星期几
		var nWeek = objDate.getDay(),
			cWeek = this.lang.nStr1[nWeek];
		if(nWeek==0) {nWeek =7;}//数字表示周几顺应天朝周一开始的惯例

		//农历年
		let year = i;
			leap = this.leapMonth(i); //闰哪个月
		let isLeap = false;

		//效验闰月
		for(i=1; i<13 && offset>0; i++) {
			//闰月
			if(leap>0 && i==(leap+1) && isLeap==false){
				--i;
				isLeap = true; temp = this.leapDays(year); //计算农历闰月天数
			}else{
				temp = this.monthDays(year, i);//计算农历普通月天数
			}
			//解除闰月
			if(isLeap==true && i==(leap+1)) { isLeap = false; }
			offset -= temp;
		}

		if(offset==0 && leap>0 && i==leap+1)
		if(isLeap){
			isLeap = false;
		}else{
			isLeap = true; --i;
		}
		if(offset<0){ offset += temp; --i; }

		//农历月
		let month = i;
		//农历日
		let day = offset + 1;

		//天干地支处理
		let sm = m-1;
		let gzY = this.toGanZhiYear(year);

		//月柱 1900年1月小寒以前为 丙子月(60进制12)
		let firstNode  = this.getTerm(year,(m*2-1));//返回当月「节」为几日开始
		let secondNode = this.getTerm(year,(m*2));//返回当月「节」为几日开始

		//依据12节气修正干支月
		let gzM = this.toGanZhi((y-1900)*12+m+11);
		if(d>=firstNode) {
			gzM = this.toGanZhi((y-1900)*12+m+12);
		}

		//传入的日期的节气与否
		let isTerm = false;
		let Term  = null;
		if(firstNode==d) {
			isTerm = true;
			Term  = this.lang.solarTerm[m*2-2];
		}
		if(secondNode==d) {
			isTerm = true;
			Term  = this.lang.solarTerm[m*2-1];
		}

		//日柱 当月一日与 1900/1/1 相差天数
		var dayCyclical = Date.UTC(y,sm,1,0,0,0,0)/86400000+25567+10;
		var gzD = this.toGanZhi(dayCyclical+d-1);
		
		//该日期所属的星座
		var astro = this.toAstro(m,d);
		return {'lYear':year,
				'lMonth':month,
				'lDay':day,
				'Animal':this.getAnimal(year),
				'IMonthCn':(isLeap?"\u95f0":'')+this.toChinaMonth(month),
				'IDayCn':this.toChinaDay(day),
				'cYear':y,
				'cMonth':m,
				'cDay':d,
				'gzYear':gzY,
				'gzMonth':gzM,
				'gzDay':gzD,
				'isToday':isToday,
				'isLeap':isLeap,
				'nWeek':nWeek,
				'ncWeek':"\u661f\u671f"+cWeek,
				'isTerm':isTerm,
				'Term':Term,
				'astro':astro
			};
	}
	/**
	 * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
	 * @param  {number}  y
	 * @param  {number}  m
	 * @param  {number}  d
	 * @param  {Boolean} isLeapMonth [如果是农历闰月第四个参数赋值true即可]
	 * @return json | object
	 */
	Calendar.prototype.lunar2solar = (y,m,d,isLeapMonth)=>{
			isLeapMonth = !!isLeapMonth;
		let leapOffset = 0,
			leapMonth  = this.leapMonth(y),
			leapDay   = this.leapDays(y);
		if(isLeapMonth&&(leapMonth!=m)) {return -1;}//传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
		if(y==2100&&m==12&&d>1 || y==1900&&m==1&&d<31) {return -1;}//超出了最大极限值
		let day = this.monthDays(y,m);
		let _day = day;

		//bugFix 2016-9-25
		if(isLeapMonth) {
			_day = this.leapDays(y,m);
		}
		if(y < 1900 || y > 2100 || d > _day) {return -1;}//参数合法性效验

		//计算农历的时间差
		let offset = 0;
		for(var i=1900;i<y;i++) {
			offset+=this.lYearDays(i);
		}
		let leap = 0,isAdd= false;
		for(var i=1;i<m;i++) {
			leap = this.leapMonth(y);
			if(!isAdd) {//处理闰月
				if(leap<=i && leap>0) {
					offset+=this.leapDays(y);isAdd = true;
				}
			}
			offset+=this.monthDays(y,i);
		}

		//转换闰月农历 需补充该年闰月的前一个月的时差
		if(isLeapMonth) {offset+=day;}

		//1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
		var stmap  =  Date.UTC(1900,1,30,0,0,0);
		var calObj =  new Date((offset+d-31)*86400000+stmap);
		var cY   =  calObj.getUTCFullYear();
		var cM   =  calObj.getUTCMonth()+1;
		var cD   =  calObj.getUTCDate();
		return this.solar2lunar(cY,cM,cD);
	}

	// ----------------------------------------------------------
	// 农历相关 END
	// ----------------------------------------------------------

	/**
	 * 输出日历头部
	 * @return string
	 */
	Calendar.prototype._outHtmlHead = ()=>{
		// 操作栏
		if( this.option.oper ){
			// 创建 操作栏
			let oper = document.createElement('div');
				oper.className = 'sys-cal-oper sys-clear';

			// 创建 日期
			let date = document.createElement('span');
				date.className = 'sys-cal-oper-date';
			oper.appendChild(date);

			// 创建 操作导航
			let ud = document.createElement('div');
				ud.className = 'sys-cal-oper-ud';

			// 创建 导航菜单
			let up = document.createElement('i');
				up.className = 'ti-angle-up up';
			let down = document.createElement('i');
				down.className = 'ti-angle-down down';

			// 添加
			ud.appendChild(up);
			ud.appendChild(down);
			oper.appendChild(ud);
			// oper.appendChild(dateDiv);

			this.option.operEl = oper;
			this.option.dateEl = date;
			this.option._dom.appendChild( oper );

			// 绑定操作
			// 上一月
			up.addEventListener('click',this._prevHtmlMonth,false);
			// 下一月
			down.addEventListener('click',this._nextHtmlMonth,false);
			// 选择日期
			// date.addEventListener('click',this._);

		}

		// 日历本体
		let table = document.createElement('table');
			table.className = 'sys-cal'; // 添加类名

		let thead = document.createElement('thead');
		let tr = document.createElement('tr');

		table.appendChild(thead);
		thead.appendChild(tr);

		// 创建日历 周期
		for (var i = 0,len=this.lang.weekArr.length; i < len; i++) {
			let th = document.createElement('th');
			th.innerHTML = this.lang.weekArr[i];
			tr.appendChild(th);
		}

		this.option._calEl = table;
		this.option._weekEl = thead;
		this.option._dom.appendChild( table );
	}

	/**
	 * 输出日历
	 * @return string
	 */
	Calendar.prototype._outHtmlCal = (year,month,day)=>{
		// 更新日期
		if( this.option.oper ){
			this.option.dateEl.innerHTML = `${year}-${month+1}`;
		}

		// 生成日历数据
		let res = [],
			currentMonth = this.getMonthCount(year,month),  // 本月
			preMonth = this.getPreMonthCount(year, month),  // 前一月
			nextMonth = this.getNextMonthCount(year, month),// 下一月
			whereMonday = this.getWeekday(year, month); // 每月1号周几
		if( whereMonday == 0 )whereMonday=7;
		
		let preArr = preMonth.slice(-1 * whereMonday);
		let nextArr = nextMonth.slice(0, 42 - currentMonth.length - whereMonday);
			res = [].concat(preArr, currentMonth, nextArr);

		// 判断是否存在日历本体
		if( this.option._dayEl == null || this.option._dayEl == 'null' ){
			// 日历本体
			let tbody = document.createElement('tbody');
				this.option._calEl.appendChild(tbody);

			// 创建日期表格
			for (var i = 0; i < 6; i++){
				// 行
				let tr = document.createElement('tr');
					tbody.appendChild(tr);

				for (var j = 0; j < 7; j++) {
					let oldHol = null;
					if( this.option.oldCal || this.option.holiday ){
						oldHol = document.createElement('p'); // 创建老历|节假日 标签
						oldHol.className = 'sub';
					}
					// 列
					let td = document.createElement('td'), // 创建表格
						span =document.createElement('span'), // 创建日期格子
						dd = 0, // 天
						mm = null,
						yy = null,
						term = null,
						date = null;
					
					if( preArr.length ){ // 上月
						dd = preArr.shift();
						mm = month==0 ? 11 : month-1;
						yy = mm==11 ? year-1 :year;
						term = this.solar2lunar(yy,mm+1,dd);
						date = `${month+1-1}-${dd}`;
						td.setAttribute('month',date);
						td.className = 'last';
					}else if( currentMonth.length ){ // 本月
						dd = currentMonth.shift();
						mm = month;
						yy = year;
						term = this.solar2lunar(yy,mm+1,dd);
						date = `${mm+1}-${dd}`;
						td.setAttribute('month',date);
						if( dd == day )td.className = 'current';
					}else{ // 下月
						dd = nextArr.shift();
						mm = month==11 ? 0 : month+1;
						yy = year;
						term = this.solar2lunar(yy,mm+1,dd);
						date = `${month+2}-${dd}`;
						td.setAttribute('month',date);
						td.className = 'lower';
					}
					// 节假日优先级，显示
					if( this.option.oldCal ){ // 老历
						oldHol.innerHTML = term['IDayCn'];
					}
					if( this._holiday.indexOf(date) !== -1 && this.option.holiday ){
						// 节假日
						let fes = this.lang.holiday[this._holiday[this._holiday.indexOf(date)]];
						oldHol.innerHTML = fes;
					}else if( this._oldHoliday.indexOf(term['lMonth']+'-'+term['lDay']) !== -1 && this.option.holiday ){
						// 老历节日
						let fes = this.lang.oldHoliday[this._oldHoliday[this._oldHoliday.indexOf(term['lMonth']+'-'+term['lDay'])]];
						oldHol.innerHTML = fes;
					}else if( term['Term'] && this.option.holiday ){
						// 气节
						oldHol.innerHTML = term['Term'];
					}
					// 除夕
					if( term['lMonth']==12 && term['lDay'] == this.monthDays( yy-1,12 ) && this.option.holiday ){
						oldHol.innerHTML = this.lang.prevNew["12-"+ this.monthDays( yy-1,12 )];
					}

					// 添加到表格
					span.innerHTML = dd;
					td.appendChild(span);
					if( this.option.oldCal || this.option.holiday )td.appendChild(oldHol);
					tr.appendChild(td);
				}
			}
			this.option._dayEl = tbody.querySelectorAll('td');
		}else{
			// 更新数据
			for (var i = 0,len=this.option._dayEl.length; i < len; i++){
				let dd = 0,
					mm = null,
					yy = null,
					term = null,
					date = null;

				if(preArr.length){ // 上月
					dd = preArr.shift();
					mm = month==0 ? 11 : month-1;
					yy = mm==11 ? year-1 :year;
					term = this.solar2lunar(yy,mm+1,dd);
					date = `${month+1-1}-${dd}`;
					this.option._dayEl[i].className = 'last';
					this.option._dayEl[i].setAttribute('month',date);
				}else if(currentMonth.length){ // 本月
					dd = currentMonth.shift();
					mm = month;
					yy = year;
					term = this.solar2lunar(yy,mm+1,dd);
					date = `${mm+1}-${dd}`;
					this.option._dayEl[i].className = '';
					if( dd == day )this.option._dayEl[i].className = 'current';
					this.option._dayEl[i].setAttribute('month',date);
				}else{ // 下月
					dd = nextArr.shift();
					mm = month==11 ? 0 : month+1;
					yy = year;
					term = this.solar2lunar(yy,mm+1,dd);
					date = `${month+2}-${dd}`;
					this.option._dayEl[i].className = 'lower';
					this.option._dayEl[i].setAttribute('month',date);
				}

				this.option._dayEl[i].firstChild.innerHTML = dd;
				if( this.option._dayEl[i].firstChild.nextSibling ){
					this.option._dayEl[i].firstChild.nextSibling.innerHTML = "";
				}

				// 节假日优先级，显示
				if( this.option.oldCal ){ // 老历
					this.option._dayEl[i].firstChild.nextSibling.innerHTML = term['IDayCn'];
				}
				if( this._holiday.indexOf(date) !== -1 && this.option.holiday ){
					// 节假日
					fes = this.lang.holiday[this._holiday[this._holiday.indexOf(date)]];
					this.option._dayEl[i].firstChild.nextSibling.innerHTML = fes;
				}else if( this._oldHoliday.indexOf(term['lMonth']+'-'+term['lDay']) !== -1 && this.option.holiday ){
					// 老历节日
					let fes = this.lang.oldHoliday[this._oldHoliday[this._oldHoliday.indexOf(term['lMonth']+'-'+term['lDay'])]];
					this.option._dayEl[i].firstChild.nextSibling.innerHTML = fes;
				}else if( term['Term'] && this.option.holiday ){
					// 气节
					this.option._dayEl[i].firstChild.nextSibling.innerHTML = term['Term'];
				}

				// 除夕
				if( term['lMonth']==12 && term['lDay'] == this.monthDays( yy-1,12 ) && this.option.holiday ){
					this.option._dayEl[i].firstChild.nextSibling.innerHTML = this.lang.prevNew["12-"+ this.monthDays( yy-1,12 )];
				}

			}
		}
	}

	/**
	 * 更新日历,并渲染DOM
	 * @return viod
	 */
	this.updateDateRender = (year,month,day)=>{
		if( typeof(year)=='undefined' || typeof(month)=='undefined' || typeof(day)=='undefined'   ){
			year = this.option.date.getFullYear();
			month = this.option.date.getMonth();
			day = this.option.date.getDate();
		}
		this._outHtmlCal( year,month,day );
	}

	/**
	 * 输出日历
	 * @return viod
	 */
	this.outHtml = ()=>{
		// 创建日历dom
		let dom = document.createElement('div');
			dom.className = 'sys-calendar';
			this.option._dom = dom;

		// 创建日历头部
		this._outHtmlHead();

		// 创建日历本体

		// 渲染日历
		this.updateDateRender();

		this.option.el.appendChild( this.option._dom );

		// 绑定滚轮事件
		if( this.option.wheel ){
			dom.onmousewheel = (e)=>{
				var e = e || window.event;
				if(e.wheelDelta){
					if(e.wheelDelta>0)this._prevHtmlMonth(); // 上
					if(e.wheelDelta<0)this._nextHtmlMonth(); // 下
				}else if(e.detail){
					if(e.detail<0)this._prevHtmlMonth(); // 上
					if(e.detail>0)this._nextHtmlMonth(); // 下
				}
			}
		}
	}

	/**
	 * 判断 平年闰年[ 四年一闰，百年不闰，四百年在闰 ]
	 * @return {[type]} [description]
	 */
	Calendar.prototype.isLeapYear = (year)=>{
		return (year % 400 === 0) || (year % 100 !== 0 && year % 4 === 0);
	}

	/**
	 * 获取每月有多少天 注意 month-[0-11]
	 * @param  {Integer} year
	 * @param  {Integer} month
	 * @return array
	 */
	Calendar.prototype.getMonthCount = (year,month)=>{
		let arr = [31,null,31,30,31,30,31,31,30,31,30,31],
			count = arr[month] || (this.isLeapYear(year) ? 29 : 28);
		return Array.from( new Array(count) , (item,value)=>value+1 );
	}

	/**
	 * 获取 上月天数
	 * @param  {Integer} year
	 * @param  {Integer} month
	 * @return array
	 */
	Calendar.prototype.getPreMonthCount = (year,month)=>{
		if( month === 0 ){
			return this.getMonthCount( year-1,11 );
		}else{
			return this.getMonthCount( year,month-1 );
		}
	}

	/**
	 * 获取 下月天数
	 * @param  {Integer} year
	 * @param  {Integer} month
	 * @return array
	 */
	Calendar.prototype.getNextMonthCount = (year,month)=>{
		if( month === 0 ){
			return this.getMonthCount( year+1,0 );
		}else{
			return this.getMonthCount( year,month+1 );
		}
	}

	/**
	 * 获取某年某月的1好周几
	 * @param  {Integer} year
	 * @param  {Integer} month
	 * @return Integer
	 */
	Calendar.prototype.getWeekday = (year, month)=>{
		let date = new Date(year,month,1);
		return date.getDay();
	}

	/**
	 * 上一月
	 * @return viod
	 */
	Calendar.prototype._prevHtmlMonth = ()=>{
		if( this.option.month==0 ){
			this.option.year-=1;
			this.option.month = 11;
		}else{
			this.option.month-=1;
		}
		this.updateDateRender( this.option.year,this.option.month,this.option.day );
	}

	/**
	 * 下一月
	 * @return viod
	 */
	Calendar.prototype._nextHtmlMonth = ()=>{
		if( this.option.month==11 ){
			this.option.year+=1;
			this.option.month = 0;
		}else{
			this.option.month+=1;
		}
		this.updateDateRender( this.option.year,this.option.month,this.option.day );
	}

	/**
	 * 获取该日期处于某节气
	 * @param  {Integer} year
	 * @param  {Integer} month
	 * @param  {Integer} day
	 * @return array
	 */
	Calendar.prototype.getSolarTerm = (year,month,day)=>{
		let solarTerms = "";
		while (solarTerms===""){
			let tmp1 = new Date((31556925974.7*(year-1900)+this._sTermInfo[month*2+1]*60000)+Date.UTC(1900,0,6,2,5));
			let tmp2 = tmp1.getUTCDate();
			if (tmp2 == day) solarTerms = this.lang.solarTerm[month*2+1];
			if(day>1){
				day=day-1; 
			}else{
				month=month-1; 
			　　if(month<0){ 
					year=year-1; month=11; 
				} 
				day=31; 
			}
			return solarTerms;
		}
	}
}